﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq;
using System.Net.NetworkInformation;
using System.ComponentModel;
using System.Diagnostics;
using Pingy.Validators;
using System.Collections.ObjectModel;
using Pingy.Properties;
using System.Windows;
using System.Windows.Threading;
using System.Windows.Input;
using System.Windows.Controls;
using System.Net;

namespace Pingy
{
	/// <summary>
	/// Interaction logic for Window1.xaml
	/// </summary>
	public partial class Main : Window, INotifyPropertyChanged
	{
		private Dictionary<string, TimeSpan> _repeatIntervals = new Dictionary<string, TimeSpan>
        {
            { "Off", TimeSpan.Zero },
            { "10 sec", TimeSpan.FromSeconds(10) },
            { "30 sec", TimeSpan.FromSeconds(30) },
            { "60 sec", TimeSpan.FromSeconds(60) },
        };

		private Pinger pinger = new Pinger();
		private IPValidator ipValidator = new IPValidator();

		private string searchString = "192.168.1.*";
		private string globalStatus = "Ready";

		private DispatcherTimer _repeatTimer = new DispatcherTimer();
		private DispatcherTimer _updateTimer = new DispatcherTimer();

		private TimeSpan _repeatInterval;
		private Stopwatch _durationTimer = new Stopwatch();

		private ObservableCollection<PingCommand> _replies = new ObservableCollection<PingCommand>();

		private DelegateCommand<PingCommand> _whoisCommand;

		public Main()
		{
			InitializeComponent();

			pinger.PingTimeOut = 10;
			pinger.FetchHostInfo = true;

			pinger.PingSent += new EventHandler<PingEventArgs>(pinger_PingSent);
			pinger.ReplyReceived += new EventHandler<PingEventArgs>(pinger_ReplyReceived);
			pinger.PingComplete += new EventHandler<PingCompleteEventArgs>(pinger_PingComplete);

			_repeatTimer.Tick += new EventHandler(_repeatTimer_Tick);
			_updateTimer.Tick += _updateTimer_Tick;

			_whoisCommand = new DelegateCommand<PingCommand>(OnExecuteWhoIsCommand);

			SelectedRepeatInterval = _repeatIntervals["Off"];

			SearchString = Settings.Default.SearchString;
			Timeout = Settings.Default.Timeout;
			FetchHostInfo = Settings.Default.FetchHostInfo;

			this.DataContext = this;
		}

		protected override void OnClosed(EventArgs e)
		{
			Settings.Default.SearchString = SearchString;
			Settings.Default.Timeout = Timeout;
			Settings.Default.FetchHostInfo = FetchHostInfo;

			Settings.Default.Save();

			base.OnClosed(e);
		}

		void _updateTimer_Tick(object sender, EventArgs e)
		{
			App.Current.Dispatcher.Invoke(DispatcherPriority.Normal, (Action)delegate()
				{
					durationTxt.Text = _durationTimer.Elapsed.ToString("mm':'ss");
				});
		}

		void _repeatTimer_Tick(object sender, EventArgs e)
		{
			_repeatTimer.Stop();

			if (SelectedRepeatInterval > TimeSpan.Zero)
			{
				StartPing();
			}
		}

		public Dictionary<string, TimeSpan> RepeatIntervals
		{
			get { return _repeatIntervals; }
		}

		public IEnumerable<PingCommand> Replies
		{
			get { return _replies; }
		}

		public int Timeout
		{
			get { return pinger.PingTimeOut; }
			set { pinger.PingTimeOut = value; SendPropertyChangedEvent("Timeout"); }
		}

		public bool FetchHostInfo
		{
			get { return pinger.FetchHostInfo; }
			set { pinger.FetchHostInfo = value; SendPropertyChangedEvent("FetchHostInfo"); }
		}

		public TimeSpan SelectedRepeatInterval
		{
			get { return _repeatInterval; }
			set { _repeatInterval = value; SendPropertyChangedEvent("SelectedRepeatInterval"); }
		}

		public string SearchString
		{
			get { return searchString; }
			set { searchString = value; SendPropertyChangedEvent("SearchString"); }
		}

		public string GlobalStatus
		{
			get { return globalStatus; }
			set { globalStatus = value; SendPropertyChangedEvent("GlobalStatus"); }
		}

		public ICommand WhoIsCommand
		{
			get { return _whoisCommand; }
		}

		private void OnExecuteWhoIsCommand(PingCommand cmd)
		{
			RunWHOIS(cmd.Address);
		}

		private void StartPing()
		{
			try
			{
				if (string.IsNullOrEmpty(SearchString))
				{
					return;
				}

				ValidationResult res = ipValidator.Validate(SearchString, System.Globalization.CultureInfo.CurrentCulture);
				if (res.ErrorContent == null)
				{
					IPRange[] range = Parser.ParseInput(SearchString);
					pinger.PingIPRange(range);
				}
				else
				{
					pinger.PingHost(SearchString);
				}

				_replies.Clear();
				GlobalStatus = "Pinging...";

				startBtn.Content = "Stop";

				_updateTimer.Start();
				_durationTimer.Restart();
			}
			catch (ArgumentException argEx)
			{
				MessageBox.Show(argEx.Message, "Parsing error", MessageBoxButton.OK, MessageBoxImage.Error);
			}
		}

		private void StopPing()
		{
			pinger.Stop();
			GlobalStatus = "Stopped";
			OnPingStopped();
		}

		private void OnPingStopped()
		{
			App.Current.Dispatcher.Invoke(DispatcherPriority.Normal, (Action)delegate()
			  {
				  startBtn.Content = "Start";
				  _updateTimer.Stop();
			  });
		}

		void pinger_ReplyReceived(object sender, PingEventArgs e)
		{
			if (e.PingCommand.Reply.Status == IPStatus.Success)
			{
				App.Current.Dispatcher.Invoke(DispatcherPriority.Normal, (Action)delegate()
				{
					_replies.Add(e.PingCommand);
				});
			}
		}

		void pinger_PingSent(object sender, PingEventArgs e)
		{
			App.Current.Dispatcher.Invoke(DispatcherPriority.Normal, (Action)delegate()
			{
				GlobalStatus = string.Format("Pinging {0}...", e.PingCommand.GetHostnameOrAddress());
			});
		}

		void pinger_PingComplete(object sender, PingCompleteEventArgs e)
		{
			DateTime dt = new DateTime(e.Elapsed.Ticks);
			if (!e.IsCancelled)
			{
				GlobalStatus = string.Format("Ping completed in {0}. Found {1} addresses", dt.ToString("mm:ss"), e.AddressesFound.Count);

				OnPingStopped();

				if (SelectedRepeatInterval > TimeSpan.Zero)
				{
					_repeatTimer.Interval = SelectedRepeatInterval;
					_repeatTimer.Start();

					GlobalStatus = "Waiting...";
				}
			}
			else
			{
				GlobalStatus = string.Format("Ping cancelled after {0}. Found {1} addresses", dt.ToString("mm:ss"), e.AddressesFound.Count);
			}
		}

		private void startBtn_Click(object sender, RoutedEventArgs e)
		{
			if (!pinger.IsRunning)
			{
				StartPing();
			}
			else
			{
				StopPing();
			}
		}

		private void exitBtn_Click(object sender, RoutedEventArgs e)
		{
			this.Close();
		}

		private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
		{
			if (pinger.IsRunning)
			{
				pinger.Stop();
			}
		}

		private void Window_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
		{
			this.DragMove();
		}

		private void ListBoxItem_MouseDoubleClick(object sender, MouseButtonEventArgs e)
		{
			RunWHOIS(((sender as ListBoxItem).Content as PingCommand).Address);
		}

		private void SendPropertyChangedEvent(string propertyName)
		{
			if (PropertyChanged != null)
			{
				PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
			}
		}

		private void RunWHOIS(IPAddress address)
		{
			WhoIs whoisDialog = new WhoIs();
			whoisDialog.Owner = this;
			whoisDialog.Address = address.ToString();
			whoisDialog.Show();
		}

		#region INotifyPropertyChanged Members

		public event PropertyChangedEventHandler PropertyChanged;

		#endregion
	}
}
